"""
Descripttion: 二进制十进制转换
Version: 1.0
Author:
Date: 2023-04-19
LastEditors:
LastEditTime:
"""
import math
from typing import List


# ----------------------------------------- 10->2

def numD2B(mint, mdeci=.0):
    y1 = numD2BInt(mint)
    y2 = numD2BDeci(mdeci)
    return y1, y2


def numD2BInt(mnum=.0):
    mlist = []
    quo = mnum
    while True:
        rem = quo % 2
        mlist.append(rem)
        quo = quo // 2
        if 0 == quo:
            return list(reversed(mlist))


def findInd(mlst: List[float], val, tol=1e-14):
    for k, v in enumerate(mlst):
        if abs(v - val) < tol:
            return True, k
    return False, -1


def numD2BDeci(mnum=.0):
    assert mnum <= 1, 'expect: par1 <=1'
    mlen = 50
    quolst = []
    remlst = [mnum, ]
    rem = mnum
    while True:
        quo = int(rem * 2 // 1)
        rem = rem * 2 % 1
        quolst.append(quo)
        remlst.append(rem)
        if len(quolst) >= mlen:
            return quolst
        res = findInd(remlst[:-1], rem, 1e-14)
        if res[0]:
            ind = res[1]
            # 含有循环节, 返回 首节, 循环节
            return quolst[:ind], quolst[ind:]


# ----------------------------------------- 2->10
# 进制转换 2 -> 10
def numB2D(int_bins, deci_bins, lastN):
    y1 = numB2DInt(int_bins)
    if lastN is None:
        y2 = numB2DDeci(deci_bins)
    else:
        y2 = numB2DRepeatDeciLastN(deci_bins, lastN)
    ret = y1 + y2
    return ret


def numB2DInt(bins):
    if not bins:
        return .0
    exp1st = len(bins) - 1
    ret = 0.0
    for i in (range(len(bins))):
        ret = ret + bins[i] * math.pow(2, exp1st - i)
    return ret


def numB2DDeci(bins):
    if not bins:
        return .0
    ret = 0.0
    for i in range(len(bins)):
        ret = ret + bins[i] * math.pow(2, -i - 1)
    assert ret < 1 + 1e-16, '计算失败, 小数部分溢出'
    return ret


# 计算循环的小数部分
def numB2DRepeatDeci(bins):
    if not bins:
        return .0
    size = len(bins)
    rhs = numB2DInt(bins)
    lhs = math.pow(2, size) - 1
    ret = rhs / lhs
    assert ret < 1 + 1e-16, '计算失败, 小数部分溢出'
    return ret


# 循环小数, 开头几位不循环
def numB2DRepeatDeciLastN(bins, lastN):
    if not bins:
        return .0
    size = len(bins)
    assert lastN <= size, '输入错误, expect: par2 <= par1.length'
    # trivial 情况: 从小数点后开始循环, 已经包含在下面, 即 len1=0
    len1 = size - lastN
    bins1 = bins[0:len1]
    y_int = numB2DInt(bins1)
    bins2 = bins[len1:size]
    y_deci = numB2DRepeatDeci(bins2)
    y = y_int + y_deci
    ret = pow(2, -len1) * y
    assert ret < 1 + 1e-16, '计算失败, 小数部分溢出'
    return ret


if __name__ == '__main__':
    # ---------------------------------------
    print('\n', 'test 10->2 -----------------------------')
    res1 = numD2BInt(53)
    print('\n', "53 -> bin", res1)

    res1 = numD2BDeci(0.7)
    print('\n', "0.7 -> bin", res1)

    res1 = numD2B(53, 0.7)
    print('\n', "53.7 -> bin", res1)

    # --------------------------------------
    print('\n', 'test 2->10 -----------------------------')
    # 整数部分, 2->10
    res1 = numB2DInt([1, 1, 1, 0])
    print("0b1110 ->", res1)
    print(0b1110)
    print()

    # 小数部分 2->10
    res2 = numB2DDeci([1, 1, 1, 1])
    print("0b0.1111->", res2)
    print(0b1111 / 2 ** 4)
    print()

    # 循环小数, 2->10
    res3 = numB2DRepeatDeci([1, 0, 1, 1])
    print("0b0.[1011]->", res3)
    print("11 / 15 ->", 11 / 15)
    print()

    # 循环小数 后几位, 2->10
    res4 = numB2DRepeatDeciLastN([1, 0, 1, 0, 1], 3)
    print("0b0.10[101]->", res4)
    print("19 / 28 ->", 19 / 28)
    print()

    # 循环小数 后几位, 2->10
    res5 = numB2D([1, 1, 1, 0], [1, 0, 1, 0, 1], 3)
    print("0b0.10[101]->", res5)
    print("14 + 19 / 28 ->", 14 + 19 / 28)
    print()

    print('\n', 'edge case 1:')
    print(numB2DInt([]))
    print(numB2DDeci([]))
    print(numB2DRepeatDeci([]))
    print(numB2DRepeatDeciLastN([], 0))

    print('\n', 'edge case 2:')
    print(numB2DRepeatDeci([1, 0, 1]))
    print(numB2DRepeatDeciLastN([1, 0, 1], 3))

    print('\n', 'edge case 3:')
    print(numB2D([], [1, 0, 1, 0, 1], 3))
    print(numB2D([1, 1, 1, 0], [], 3))
    print(numB2D([1, 1, 1, 0], [1, 0, 1, 0, 1], 4))

    # print(numB2D([1, 1, 1, 0], [1, 0, 1], 4))

    # 切片测试
    print('\n', 'slice test:')
    a = [5, 6, 8]
    print(a[0])
    print(a[0:0])

    #
    print(1.5 % 1)
    aList = [123, 'xyz', 'xyz', 'abc']
    print("xyz 索引位置: ", aList.index('xyz'))
    b = iter(aList)
    print(b, "->", list(b))
